import fitz
from PIL import Image
from unrar import rarfile
from os import walk, makedirs
from pywinauto import Desktop
from pyzipper import AESZipFile
from subprocess import run, PIPE
from warnings import filterwarnings
from zipfile import ZipFile, is_zipfile
from rarfile import RarFile, is_rarfile
from py7zr import SevenZipFile, is_7zfile
from shutil import move, rmtree, copytree
from pywinauto.application import Application
from os.path import basename, dirname, join, abspath, exists, isfile, isdir


# 检测ini配置文件
def check_ini():
    ini = '.\\脚本配置.ini'
    if not exists(ini):
        content = input('\n\n请选择是否需要保存PDG文件夹，0为不保存，1为保存，请输入相应数字后回车：')
        if content == '0':
            content = 'SAVE_PDG=FALSE'
        else:
            content = 'SAVE_PDG=TRUE'
        with open(ini, 'a', encoding='UTF-8') as f:
            f.write(content)
    else:
        with open(ini, 'r', encoding='UTF-8') as f:
            contents = f.readlines()
            content = contents[0]
            if content != 'SAVE_PDG=FALSE' and content != 'SAVE_PDG=TRUE':
                content = input('\n\n请选择是否需要保存PDG文件夹，0为不保存，1为保存，请输入相应数字后回车：')
                if content == '0':
                    content = 'SAVE_PDG=FALSE'
                else:
                    content = 'SAVE_PDG=TRUE'
        with open(ini, 'w', encoding='UTF-8') as f:
            f.write(content)
    return content


# 检测文件类型
def check_file_format(package):
    file_format = 0
    file_name = basename(package)
    package = f'\\\\?\\{package}'    # 启用长路径进行判断，防止路径过长，导致判断出错
    if is_zipfile(package):
        print(f'{file_name} 为ZIP压缩包！')
        file_format = 'zip'
    elif is_rarfile(package):
        print(f'{file_name} 为RAR压缩包！')
        file_format = 'rar'
    elif is_7zfile(package):
        print(f'{file_name} 为7z压缩包！')
        file_format = '7z'
    elif isdir(package):
        print(f'{file_name} 为解压后的文件夹！\n')
        file_format = 'dir'
    else:
        print(f'{file_name} 非ZIP、RAR、UVZ、7z压缩包！')
        print('暂无法提供解密、解压支持，将直接跳过！\n')
    return file_format


# 解决ZIP压缩包解压后中文乱码问题
def support_gbk(zip_file):                        # zip_file 可以 ZipFile 对象或 AESZipFile 对象
    code1, code2 = 'cp437', 'gbk'                 # 预设编码、解码方式
    origin = zip_file.NameToInfo                  # 获取zip_file的NameToInfo字典
    copy = origin.copy()                          # 创建字典副本
    # 寻找合适的编码、解码方式
    for name, _ in copy.items():                  # 获取乱码文件名
        try:
            name.encode(code1).decode(code2)      # cp437有的ZIP压缩包会报错
        except:
            try:
                code1 = code2
                name.encode(code1).decode(code2)
            except:
                print('预设编码、解码方式，无法解决中文乱码问题，请自行修改解压后乱码的文件名!')
    # 采用合适的编码方式编码并存入字典
    for name, info in copy.items():
        real_name = name.encode(code1).decode(code2)
        if real_name != name:
            info.filename = real_name              # 修改info中的filename信息
            del origin[name]                       # 删除原字典中的乱码的name和info
            origin[real_name] = info               # 写入不乱码的name和info
    return zip_file


# 读取解压密码
def get_pwds():
    # 优先遍历的密码本
    # 去重检查
    with open(".\\Passwords\\passwords1.txt", 'r', encoding='UTF-8') as f:
        pwds1 = f.readlines()
        pwds1 = [i for i in pwds1 if i != '\n']    # 去除空行
        pwds1 = list(set(pwds1))                   # 去除重复
    # 重新写入
    with open(".\\Passwords\\passwords1.txt", 'w', encoding='UTF-8') as f:
        for pwd in pwds1:
            f.write(pwd)
    # 再次读取
    with open(".\\Passwords\\passwords1.txt", 'r', encoding='UTF-8') as f:
        pwds1 = f.readlines()
        pwds1 = [i.rstrip('\n') for i in pwds1]
    # 6万个密码的密码本
    # 去重检查
    with open(".\\Passwords\\passwords2.txt", 'r', encoding='UTF-8') as f:
        pwds2 = f.readlines()
        pwds2 = [i for i in pwds2 if i != '\n']    # 去除空行
        pwds2 = list(set(pwds2))                   # 去除重复
    # 重新写入
    with open(".\\Passwords\\passwords2.txt", 'w', encoding='UTF-8') as f:
        pwds2.sort(key=len)                        # 重新排序
        for pwd in pwds2:
            f.write(pwd)
    # 再次读取
    with open(".\\Passwords\\passwords2.txt", 'r', encoding='UTF-8') as f:
        pwds2 = f.readlines()
        pwds2 = [i.rstrip('\n') for i in pwds2]
        pwds2 = [i for i in pwds2 if i not in pwds1]  # 去重：优先遍历密码本 & 6万个密码的密码本
    # 构造生成器
    pwds = pwds1 + pwds2
    pwds = (i for i in pwds)
    return pwds


# 检测，写入优先遍历的密码本，以及导出密码
def pwds_refresh_export(pwd, file_name):
    global save_dir0
    # 写入优先遍历密码本
    pwds1_txt = '.\\passwords\\passwords1.txt'
    with open(pwds1_txt, 'r', encoding='UTF-8') as f:
        pwds1 = f.readlines()
    right_pwd = pwd + '\n'
    if right_pwd not in pwds1:
        pwds1.append(right_pwd)
        with open(pwds1_txt, 'w', encoding='UTF-8') as f:
            for pwd in pwds1:
                f.write(pwd)
        print(f'{pwd}已写入优先遍历的密码本 {abspath(pwds1_txt)}')
    # 导出解压密码
    with open(f"\\\\?\\{abspath(join(save_dir0, '解压密码.txt'))}", 'a', encoding='UTF-8') as f:    # 采用长路径，防止报错
        f.write(f'{file_name}  解压密码：{pwd}\n')
        print(f'{file_name} 解压密码已导出！')


# 爆破失败的处理
def can_not_break(decompress_dir):
    global save_dir0
    # 删除因爆破所产生的空文件夹
    rmtree(f'\\\\?\\{decompress_dir}')
    # 导出相应信息
    with open(f"\\\\?\\{abspath(join(save_dir0, '爆破失败.txt'))}", 'a', encoding='UTF-8') as f:    # 采用长路径，防止报错
        f.write(f'{basename(decompress_dir)}  爆破失败！\n')


# 检测ZIP压缩包是否加密
def check_encryption_zip(package, decompress_dir):
    file_name = basename(package)
    package = f'\\\\?\\{package}'  # 启用长路径进行判断，防止路径过长，导致判断出错
    with support_gbk(ZipFile(package)) as zf:
        # 检测压缩包是否为空包
        zinfo = zf.infolist()
        if len(zinfo) == 0:
            print(f'{file_name} 压缩包已损坏，或存在其他问题，无法正常解压，将直接跳过！\n')
            check = 'empty'
        # 检测压缩包是否加密
        else:
            # 因为压缩包里面可能包含目录，但目录是不会加密的，所以要剔除目录部分后再进行判断
            zinfo_temp = [i for i in zinfo if not i.is_dir()]
            if zinfo_temp[0].flag_bits & 0x1:
                check = 'encrypted'
                print(f'{file_name} 已加密！')
            else:
                print(f'{file_name} 未加密！')
                check = 'unencrypted'
                # 如果压缩包没有加密，则直接解压
                # 解压文件夹，统一为压缩包名
                if file_name.endswith('.zip'):
                    decompress_dir = join(decompress_dir, file_name.replace('.zip', ''))
                elif file_name.endswith('.uvz'):
                    decompress_dir = join(decompress_dir, file_name.replace('.uvz', ''))
                # 分析压缩包内是否有文件夹
                names = [i.filename for i in zinfo]
                dir_check = 0
                for name in names:
                    if '/' in name.rstrip('/'):
                        dir_check = 1
                        break
                if dir_check == 0:
                    print(f'需创建新文件夹收集压缩包内的文件，其路径为：{abspath(decompress_dir)}')
                print(f'正在解压压缩包内的 {len([i for i in zinfo if i.is_dir() is False])} 个文件...')
                count = 0
                # 逐个解压压缩包内的文件
                for member in zinfo:
                    zf.extract(member, f'\\\\?\\{decompress_dir}')  # 启用长路径解压，防止报错
                    count = count + 1
                    print(count, end='\r')
                print(f'解压成功，{file_name} 的解压路径为：{abspath(decompress_dir)}\n')
    return check, decompress_dir


# 检测RAR压缩包是否加密
def check_encryption_rar(package, decompress_dir):
    file_name = basename(package)
    package = f'\\\\?\\{package}'    # 启用长路径进行判断，防止路径过长，导致判断出错
    with RarFile(package) as rf:
        # 检测是否存在空包问题
        rinfo = rf.infolist()
        if len(rinfo) == 0:
            print(f'{file_name} 压缩包已损坏，或存在其他问题，无法正常解压，将直接跳过！\n')
            check = 'empty'
        # 检测压缩包是否加密
        else:
            if rf.needs_password():
                print(f'{file_name} 已加密！')
                check = 'encrypted'
            else:
                print(f'{file_name} 未加密！')
                check = 'unencrypted'
                # 如果压缩包未加密，则直接解压
                # 解压文件夹统一为压缩包名
                decompress_dir = join(decompress_dir, file_name.replace('.rar', ''))
                # 检测压缩包内有无文件夹
                names = [i.filename for i in rinfo]
                dir_check = 0
                for name in names:
                    if '/' in name.rstrip('/'):
                        dir_check = 1
                        break
                if dir_check == 0:
                    print(f'需创建新文件夹收集压缩包内的文件，其路径为：{abspath(decompress_dir)}')
                print(f'正在解压压缩包内的 {len([i for i in rinfo if i.is_dir() is False])} 个文件...')
                count = 0
                # 逐个解压压缩包内文件
                for member in rinfo:
                    rf.extract(member, f'\\\\?\\{decompress_dir}')        # 启用长路径，防止解压报错
                    count = count + 1
                    print(count, end='\r')
                print(f'解压成功，{file_name} 的解压路径为：{abspath(decompress_dir)}\n')
    return check, decompress_dir


# 检测7z压缩包是否加密
def check_encryption_7z(package, decompress_dir):
    file_name = basename(package)
    package = f'\\\\?\\{package}'  # 启用长路径进行判断，防止路径过长，导致判断出错
    with SevenZipFile(package) as sf:
        # 检测压缩包是否存在空包问题
        sinfo = sf.list()
        if len(sinfo) == 0:
            print(f'{file_name} 压缩包已损坏，或存在其他问题，无法正常解压，将直接跳过！\n')
            check = 'empty'
        # 检测压缩包是否加密
        else:
            if sf.needs_password():
                print(f'{file_name} 已加密！')
                check = 'encrypted'
            else:
                print(f'{file_name} 未加密！')
                check = 'unencrypted'
                # 如果压缩包未加密，则直接解压
                # 解压文件夹统一为压缩包名
                decompress_dir = join(decompress_dir, file_name.replace('.7z', ''))
                # 检测压缩包内是否有文件夹
                names = sf.getnames()
                dir_check = 0
                for name in names:
                    if '/' in name.rstrip('/'):
                        dir_check = 1
                        break
                if dir_check == 0:
                    print(f'需创建新文件夹收集压缩包内的文件，其路径为：{abspath(decompress_dir)}')
                names_temp = [name for name in names if "/" in name.rstrip("/")]
                print(f'正在解压压缩包内的 {len(names_temp)} 个文件...')
                count = 0
                # 逐个解压压缩包内文件
                for name in names_temp:
                    # 无需启用长路径，7z.exe会自己处理的
                    cmd = ['.\\Tools\\7z.exe', 'e', f'-o{decompress_dir}', '-aoa', package, name]
                    run(cmd, check=True, stdout=PIPE, stderr=PIPE)
                    count = count + 1
                    print(count, end='\r')
                print(f'解压成功，{file_name} 的解压路径为：{abspath(decompress_dir)}\n')
    return check, decompress_dir


# 尝试爆破采用经典加密ZIP压缩包（包括AES加密）
def decryption_zip(package, decompress_dir, pwds, code):
    count = 0
    file_name = basename(package)
    package = f'\\\\?\\{package}'         # 启用长路径进行判断，防止路径过长，导致判断出错
    with support_gbk(ZipFile(package)) as zf:
        zinfo = zf.infolist()
        # 解压缩文件夹指定为压缩包名
        if file_name.endswith('.zip'):
            decompress_dir = join(decompress_dir, file_name.replace('.zip', ''))
        elif file_name.endswith('.uvz'):
            decompress_dir = join(decompress_dir, file_name.replace('.uvz', ''))
        # 判断压缩包内有无文件夹
        names = [i.filename for i in zinfo]
        dir_check = 0
        for name in names:
            if '/' in name.rstrip('/'):
                dir_check = 1
                break
        if dir_check == 0:
            print(f'需创建新文件夹收集压缩包内的文件，其路径为：{abspath(decompress_dir)}')
        # 找到一个目录（一般不加密）以外的加密文件
        zinfo_file_names = [name for name in names if name.endswith((
            '.dat', '.jpg', '.jpeg', '.png', '.pdg', '.tif', '.tiff', '.webp', '.pdf', '.epub', '.mobi', '.rar', '.uvz',
            '.7z', '.txt'
        ))]
        member = zinfo_file_names[0]
        for i in zinfo:
            if i.filename == member:
                member = i
                break
        # 爆破密码
        print(f'正在逐个尝试密码...')
        while True:
            try:
                pwd = next(pwds)
                count = count + 1
                print(f'已遍历 {count} 个密码', end='\r')
                extract_item = zf.extract(member, f'\\\\?\\{decompress_dir}', pwd.encode(code))     # 启用长路径，防止报错
                check = 'extracted'
                break
            except StopIteration:
                print(f'已遍历密码本，未找到解压密码!')
                check = 'None'
                break
            except NotImplementedError:
                print(f'{file_name} 采用了AES加密！')
                check = 'AES'
                break
            except:
                pass
        # 如果成功爆破，则解压压缩包内文件
        if check == 'extracted':
            print(f'{file_name} 的解压密码为：{pwd}')
            pwds_refresh_export(pwd, file_name)  # 将解压密码写入优先遍历的密码本
            # 在开始解压前，还得先检测一下decompress_dir文件夹内还有没有其他文件夹，因为zf.extract是保留文件结构解压的
            for root, dirs, files in walk(f'\\\\?\\{decompress_dir}'):
                if dirs != 0:
                    for dir in dirs:
                        rmtree(join(root, dir))
                break
            print(f'正在解压压缩包内的 {len(zinfo_file_names)} 个文件...')
            count_temp = 0
            # 逐个解压压缩包内的文件
            for name in zinfo_file_names:
                cmd = ['.\\Tools\\7z.exe', 'e', f'-o{decompress_dir}', f'-p{pwd}', '-aoa', package, name]
                run(cmd, check=True, stdout=PIPE, stderr=PIPE)
                count_temp = count_temp + 1
                print(count_temp, end='\r')
            print(f'解压成功，{file_name} 的解压路径为：{abspath(decompress_dir)}\n')
        # 如果是AES加密的ZIP压缩包，则采用Pyzipper继续尝试爆破
        elif check == 'AES':
            zf.close()
            count = 0
            pwds = get_pwds()  # 因为经典加密压缩包已经迭代了一次生成器，所以要重新获取一个，防止第一个值丢失。
            with support_gbk(AESZipFile(package)) as af:
                ainfo = af.infolist()
                # 找到一个被加密的文件
                ainfo_file_names = [i.filename for i in ainfo if (i.filename).endswith((
                    '.dat', '.jpg', '.jpeg', '.png', '.pdg', '.tif', '.tiff', '.webp', '.pdf', '.epub', '.mobi',
                    '.rar', '.uvz', '.7z', '.txt'
                ))]
                member = ainfo_file_names[0]
                for i in ainfo:
                    if i.filename == member:
                        member = i
                        break
                # 开始爆破密码
                print(f'正在逐个尝试密码...')
                while True:
                    try:
                        pwd = next(pwds)
                        count = count + 1
                        print(f'已遍历 {count} 个密码', end='\r')
                        extract_item = af.extract(member, f'\\\\?\\{decompress_dir}', pwd.encode(code))
                        check = 'extracted'
                        break
                    except StopIteration:
                        print(f'已遍历密码本，未找到解压密码!')
                        check = 'None'
                        break
                    except:
                        pass
                if check == 'extracted':
                    print(f'{file_name} 的解压密码为：{pwd}')
                    pwds_refresh_export(pwd, file_name)  # 将解压密码写入优先遍历的密码本
                    # 在开始解压前，还得先检测一下decompress_dir文件夹内还有没有其他文件夹，因为zf.extract是保留文件结构解压的
                    for root, dirs, files in walk(f'\\\\?\\{decompress_dir}'):
                        if dirs != 0:
                            for dir in dirs:
                                rmtree(join(root, dir))
                        break
                    print(f'正在解压压缩包内的 {len(ainfo_file_names)} 个文件...')
                    count_temp = 0
                    for name in ainfo_file_names:
                        cmd = ['.\\Tools\\7z.exe', 'e', f'-o{decompress_dir}', f'-p{pwd}', '-aoa', package, name]
                        run(cmd, check=True, stdout=PIPE, stderr=PIPE)
                        count_temp = count_temp + 1
                        print(count_temp, end='\r')
                    print(f'解压成功，{file_name} 的解压路径为：{abspath(decompress_dir)}\n')
    return check, decompress_dir


# 尝试爆破RAR加密压缩包
def decryption_rar(package, decompress_dir, pwds, code):
    count = 0
    file_name = basename(package)
    package = f'\\\\?\\{package}'   # 启用长路径，防止路径过长，导致判断出错
    # 第一个RAR模块
    with RarFile(package) as rf:
        rinfo = rf.infolist()
        # 将解压文件夹指定为压缩包名
        decompress_dir = join(decompress_dir, file_name.replace('.rar', ''))
        # 判断压缩包内有无文件夹
        names = [i.filename for i in rinfo]
        dir_check = 0
        for name in names:
            if '/' in name.rstrip('/'):
                dir_check = 1
                break
        if dir_check == 0:
            print(f'需创建新文件夹收集压缩包内的文件，其路径为：{abspath(decompress_dir)}')
    # 第二个RAR模块
    with rarfile.RarFile(package) as rf:
        rinfo = rf.infolist()
        # 找出压缩包中非目录的一个加密文件
        rinfo_file_names = [name.filename for name in rinfo if (name.filename).endswith((
            '.dat', '.jpg', '.jpeg', '.png', '.pdg', '.tif', '.tiff', '.webp', '.pdf', '.epub', '.mobi', '.rar', '.uvz',
            '.7z', '.txt'
        ))]
        member = rinfo_file_names[0]
        for i in rinfo:
            if i.filename == member:
                member = i
                break
        # 爆破密码
        print(f'正在逐个尝试密码...')
        while True:
            try:
                pwd = next(pwds)
                count = count + 1
                print(f'已遍历 {count} 个密码', end='\r')
                extract_item = rf.extract(member, f'\\\\?\\{decompress_dir}', pwd.encode(code))
                check = 'extracted'
                break
            except StopIteration:
                print(f'已遍历密码本，未找到解压密码!\n')
                check = 'None'
                break
            except:
                pass
        # 如果爆破成功，则解压压缩包内文件
        if check == 'extracted':
            print(f'{file_name} 的解压密码为：{pwd}')
            pwds_refresh_export(pwd, file_name)
            print(f'正在解压压缩包内的 {len(rinfo_file_names)} 个文件...')
            count_temp = 0
            # 逐个解压压缩包内的文件
            for member in rinfo:
                rf.extract(member, f'\\\\?\\{decompress_dir}', pwd.encode(code))  # 启用长路径，防止报错
                count_temp = count_temp + 1
                print(count_temp, end='\r')
            print(f'解压成功，{file_name} 的解压路径为：{abspath(decompress_dir)}\n')
    return check, decompress_dir


# 尝试爆破7z加密压缩包
def decryption_7z(package, decompress_dir, pwds, code):
    count = 0
    file_name = basename(package)
    package = f'\\\\?\\{package}'      # 启用长路径，防止路径过长，导致判断出错
    with SevenZipFile(package) as sf:
        names = sf.getnames()
        # 判断7z压缩包内有无文件夹
        dir_check = 0
        for name in names:
            if '/' in name.rstrip('/'):
                dir_check = 1
                break
        if dir_check == 0:
            print(f'需创建新文件夹收集压缩包内的文件，其路径为：{abspath(decompress_dir)}')
        # 目录未加密，所以要去除目录，找到一份被加密的文件
        sinfo_file_names = [name for name in names if name.endswith((
            '.dat', '.jpg', '.jpeg', '.png', '.pdg', '.tif', '.tiff', '.webp', '.pdf', '.epub', '.mobi', '.rar', '.uvz',
            '.7z', '.txt'
        ))]
        member = sinfo_file_names[0]
        # 准备爆破7z压缩包
        print(f'正在逐个尝试密码...')
        while True:
            try:
                pwd = next(pwds)
                count = count + 1
                print(f'已遍历 {count} 个密码', end='\r')
                cmd = ['.\\Tools\\7z.exe', 'e', f'-o{decompress_dir}', f'-p{pwd}', '-aoa', package, member]
                run(cmd, check=True, stdout=PIPE, stderr=PIPE)
                check = 'extracted'
                break
            except StopIteration:
                print(f'已遍历密码本，未找到解压密码!\n')
                check = 'None'
                break
            except:
                pass
        # 如果爆破成功，则解压压缩包内文件
        if check == 'extracted':
            print(f'{file_name} 的解压密码为：{pwd}')
            pwds_refresh_export(pwd, file_name)  # 将解压密码写入优先遍历的密码本
            print(f'正在解压压缩包内的 {len(sinfo_file_names)} 个文件...')
            count_temp = 0
            # 逐个解压压缩包内文件
            for name in sinfo_file_names:
                cmd = ['.\\Tools\\7z.exe', 'e', f'-o{decompress_dir}', f'-p{pwd}', '-aoa', package, name]
                run(cmd, check=True, stdout=PIPE, stderr=PIPE)
                count_temp = count_temp + 1
                print(count_temp, end='\r')
            print(f'解压成功，{file_name} 的解压路径为：{abspath(decompress_dir)}\n')
    return check, decompress_dir



# 图片转PDF
def pic2pdf(img_paths, pdf_save_path, tocs, delete_dir):
    global SAVE_PDG
    with fitz.open() as new_pdf:
        print(f'正在将 {len(img_paths)} 张图片转成PDF，图片转PDF为无损转换，图片质量越高，速度越慢~')
        count = 0
        for img_path in img_paths:
            count = count + 1
            print(count, end='\r')
            with fitz.open(img_path) as img:             # 图片转 PDF 字节
                pdf_bytes = img.convert_to_pdf()
                img_pdf = fitz.open("pdf", pdf_bytes)    # PDF 字节转 PDF 文档
                new_pdf.insert_pdf(img_pdf)
        # 统计最小页面
        min_w = float('inf')                             # 变量初始化为无穷大
        min_h = float('inf')
        for page in new_pdf:
            w, h = page.rect.width, page.rect.height
            if w < min_w:
                min_w = w
            if h < min_h:
                min_h = h
        # 缩放PDF页面等
        page_num = 0
        with fitz.open() as scale_pdf:
            # 缩放PDF页面
            for page in new_pdf:
                _, _, w, h = page.rect
                min_ratio, page_ratio = (min_w / min_h), (w / h)
                if min_ratio == page_ratio:
                    w_new, h_new = min_w, min_h
                elif min_ratio < page_ratio:
                    w_new, h_new = min_w, (h / (w / min_w))  # 影响上下留白 # 上下不留白
                else:
                    w_new, h_new = min_w, (h / (w / min_w))  # 影响左右留白 # 左右不留白，且大小要拉直
                scale_pdf_page = scale_pdf.new_page(-1, width=w_new, height=h_new)
                rect = fitz.Rect(0, 0, w_new, h_new)
                scale_pdf_page.show_pdf_page(rect, new_pdf, page_num, keep_proportion=True)
                page_num += 1
            # 添加书签
            scale_pdf.set_toc(tocs, collapse=1)
            scale_pdf.save(pdf_save_path)
    # 根据是否要保留PDG文件夹，执行不同操作
    if SAVE_PDG == 'SAVE_PDG=FALSE':
        rmtree(delete_dir)
    else:   # 要保留PDG文件夹，所以要重新把后缀改为PDG
        pic_file_paths = [0, []]
        for root, dirs, files in walk(delete_dir):  # 启用长路径检索
            if len(dirs) == 0 and len(files) != 0:
                pic_file_paths = [root,
                    [i for i in files if i.endswith(('.pdg', '.png', '.jpg', '.jpeg', '.webp', '.tif', '.tiff'))]]
        pics = pic_file_paths[1]
        pic_paths = [join(root, pic) for pic in pics]
        for pic_path in pic_paths:
            pic_path_new = pic_path.replace(pic_path[-4:], '.pdg')
            move(pic_path, pic_path_new)


# 从PDG文件夹转成PDF
def dirs2pdf(decompress_dir):
    global horses, save_dir0, file_name0
    # 找出decompress_dir文件夹内的图片
    pic_file_paths = [0, []]
    for root, dirs, files in walk(f'\\\\?\\{decompress_dir}'):   # 启用长路径检索
        if len(dirs) == 0 and len(files) != 0:                   # 没有文件夹，只有文件，不需要继续遍历
            pic_file_paths = [root.replace('\\\\?\\', ''),
                [i for i in files if i.endswith(('.pdg', '.png', '.jpg', '.jpeg', '.webp', '.tif', '.tiff'))]]
    # decompress_dir文件夹内没有图片
    if len(pic_file_paths[1]) == 0:
        print(f'{pic_file_paths[0]} 文件夹内无任何图片！')
        # 检查一下decompress_dir文件夹内，还有没有其他格式的文件，如PDF，EPUB，MOBI等
        other_file_paths = [0, []]
        for root, dirs, files in walk(f'\\\\?\\{decompress_dir}'):   # 启用长路径遍历，防止报错
            if len(dirs) == 0 and len(files) != 0:
                other_file_paths = [root.replace('\\\\?\\', ''), [i for i in files]]
        # 如果decompress_dir文件夹内，无任何其他格式文件
        if len(other_file_paths[1]) == 0:
            print(print(f'{other_file_paths[0]} 文件夹内为空！'))
        # 如果decompress_dir文件夹内，有其他格式文件
        else:
            total = ''                                              # 统计有哪些文件
            root, names = other_file_paths
            for name in names:
                total = total + name + '，'
                path_old, path_new = join(root, name), join(save_dir0, name)
                move(f'\\\\?\\{path_old}', f'\\\\?\\{path_new}')    # 将压缩包内的文件移动到保存文件夹中，已启用长路径
            print('\033[92m' + f'{root} 文件夹内所有的文件为：{total[:-1]}\n\n' + '\033[0m')
            rmtree(f'\\\\?\\{decompress_dir}')                      # 文件移动完毕后，启用长路径删除原文件夹
    # decompress_dir文件夹内没有图片
    else:
        # 确定PDF保存路径，启用长路径，防止在PyMuPDF的合并过程中报错
        pdf_save_path = f'\\\\?\\{decompress_dir}.pdf'
        # 检测书籍是否缺页
        PAGE_LOST = 0
        pics = pic_file_paths[1]
        bodys = [pic for pic in pics if pic[:-4].isdigit()]   # 检测范围局限于书籍主体部分
        if len(bodys) != 0:
            temp_list1 = [i for i in range(int(bodys[0][:-4]), int(bodys[-1][:-4]) + 1)]
            temp_list2 = [int(i[:-4]) for i in bodys]
            temp_list3 = list(set(temp_list1) - set(temp_list2))
            temp_list3.sort()
            if len(temp_list3) != 0:
                print('\033[0m' + f'{basename(decompress_dir)} 部分页面缺失！' + '\033[0m')
                # 统计缺失的页面
                lose_page = f'{basename(decompress_dir)} 缺失页面为：'
                for i in temp_list3:
                    lose_page = lose_page + str(i) + '，'
                PAGE_LOST = lose_page[:-1]
                # 写入相应txt文档，方便查阅
                with open(f'\\\\?\\{join(save_dir0, "缺页提示.txt")}', 'a', encoding='UTF-8') as f:   # 启用长路径，防止报错
                    f.write(lose_page[:-1] + '\n')
            else:
                print('已确认书籍完整无缺页！')
        # 检测PDG图片是否加密
        pdg_check = 0
        try:
            root = pic_file_paths[0]
            pdgs = [join(root, pic) for pic in pics if pic.endswith('.pdg')]
            if len(pdgs) == 0:  # 说明里面都是非PDG图片
                pdg_check = 'None'
                print('压缩包中皆为非 PDG 图片，无需检测是否加密！')
            else:  # 通过使用Image打开，来检测PDG是否加密
                for pdg in pdgs:
                    with Image.open(f'\\\\?\\{pdg}') as img:    # 启用长路径，防止报错
                        pass
                pdg_check = 'unencrypted'
                print('已确认无加密PDG！')
        except Exception as e:
            print('\033[91m' + f'{basename(root)} 内含加密PDG！稍后将自动尝试调用老马合成PDF!\n\n' + '\033[0m')
            # 保存三种信息：存在PDG的文件夹+解压缩的文件夹+PDF保存路径
            horses.append((abspath(root), decompress_dir, pdf_save_path.replace("\\\\?\\", '')))
        # 如果是非PDG图片，或者是PDG但是可以打开
        if pdg_check in ['None', 'unencrypted']:
            # 智能识别图片类型。不修改文件后缀名，PyMuPDF识别不出来
            if pdg_check == 'unencrypted':
                print('正在检测修改PDG后缀！')
                for pdg in pdgs:
                    pdg = f'\\\\?\\{pdg}'        # 启用长路径，防止报错, PDG启用长路径后，经替换得出的new_pdg也是长路径
                    with Image.open(pdg) as img:
                        img_format = img.format
                        # 根据老马的PDG科普文档，大图版的PDG就只有PNG和JPG两种类型
                        if img_format in ['JPEG', 'JPG', 'jpeg', 'jpg']:
                            new_pdg = pdg.replace('.pdg', '.jpg')
                        else:
                            new_pdg = pdg.replace('.pdg', '.png')
                    move(pdg, new_pdg)
                # 重新获取pic_file_paths
                for root, dirs, files in walk(f'\\\\?\\{decompress_dir}'):
                    if len(dirs) == 0 and len(files) != 0:
                        pic_file_paths = [root.replace('\\\\?\\', ''),
                            [i for i in files if i.endswith(('.pdg', '.png', '.jpg', '.jpeg', '.webp', '.tif', '.tiff'))]]
            # 检测图片是否损坏（主要是截断图）
            PAGE_BROKEN = 0
            broken, repair = [], []
            root, pics_names = pic_file_paths          # 重新获取root和pics
            pics_paths = [join(root, pic_name) for pic_name in pics_names]
            for pic_path in pics_paths:
                try:
                    with Image.open(f'\\\\?\\{pic_path}') as img:
                        img.verify()
                except:
                    print(f'{abspath(pic_path)} 图片疑似部分损坏！正在尝试修复！')
                    broken.append(abspath(pic_path))
                    with Image.open(f'\\\\?\\{pic_path}') as img:
                        widths, heights = img.size
                        img_new = Image.new('RGB', img.size, (255, 255, 255))
                        try:
                            img_rgb = img.convert('RGB')
                            pixels = img_rgb.load()
                            try:
                                for h in range(heights):
                                    for w in range(widths):
                                        if pixels[h, w] != (255, 255, 255):
                                            img_new.putpixel((h, w), pixels[h, w])
                            except:
                                pass
                            img_new.convert(img.mode)
                            img_new.save(f'\\\\?\\{pic_path}')
                            repair.append(1)
                            print(f'{abspath(pic_path)} 修复完成！')
                        except:
                            repair.append(0)
                            print('\033[91m' + f'{abspath(pic_path)} 修复失败！' + '\033[0m')
            # 导出损坏图片信息
            if len(broken) != 0:
                text_temp = f'{basename(decompress_dir)} 损坏的图片为：'
                for pic_path in broken:
                    text_temp = text_temp + str(basename(pic_path)) + '，'
                PAGE_BROKEN = text_temp[:-1]
                with open(f'\\\\?\\{join(save_dir0, "损坏图片.txt")}', 'a', encoding='UTF-8') as f:  # 启用长路径，防止报错
                    f.write(text_temp[:-1] + '\n')
            # 如果有损坏且无法修复的图片，将调用老马尝试修复
            if len(repair) != 0 and 0 in repair:
                print('\033[91m' + f'{basename(dirname(pics_paths[0]))} 内含无法修复的损坏图片！稍后将自动尝试调用老马合成PDF!\n\n' + '\033[0m')
                # 将文件的后缀重新修改为pdg
                for pic_path in pics_paths:
                    pdg_path = pic_path.replace('.' + pic_path.split('.')[-1], '.pdg')
                    move(f'\\\\?\\{pic_path}', f'\\\\?\\{pdg_path}')
                # 将文件夹路径添加到待调用老马的列表中
                horses.append((dirname(abspath(pdgs[0])), decompress_dir, pdf_save_path.replace("\\\\?\\", '')))
            # 如果没有损坏图片，或者有损坏图片但可以修复
            else:
                print('已确认无损坏图片！')
                # 需要收集的内容
                collector = {}
                parts = ['covers', 'boks', 'legs', 'fows', 'catalogs', 'bodys', 'atts', 'bacs', 'inss', 'remains']
                for part in parts:
                    collector[part] = []
                # 开始收集（根据频率、数量进行先后排序）
                root, pics = pic_file_paths
                for pic in pics:
                    test = pic[:-4]
                    if test.isdigit():      # 书籍主体
                        collector['bodys'].append(pic)
                    elif 'fow' in test:     # 书籍序言等
                        collector['fows'].append(pic)
                    elif 'att' in test:     # 书籍附录
                        collector['atts'].append(pic)
                    elif '!' in test:       # 书籍目录
                        collector['catalogs'].append(pic)
                    elif 'cov' in test:     # 书籍封面
                        # ‘cover’ 和 ‘cov’ 都有可能是缩略图（非常模糊，非常小，没什么用）
                        with Image.open(f'\\\\?\\{join(root, pic)}') as img_temp:
                            w, h = img_temp.size
                            if w < 10 or h < 10:   # 宽高之一小于10，极有可能是缩略图，直接删掉
                                pass
                            else:
                                collector['covers'].append(pic)
                    elif 'bok' in test:     # 灰色封面
                        collector['boks'].append(pic)
                    elif 'leg' in test:     # 书籍后面的出版说明
                        collector['legs'].append(pic)
                    elif 'bac' in test:     # 封底
                        collector['bacs'].append(pic)
                    elif 'ins' in test:     # 插页
                        collector['inss'].append(pic)
                    else:                   # 书籍剩余部分
                        collector['remains'].append(pic)
                # 对各个部分收集到的内容分别排序
                for key in collector:
                    collector[key].sort()
                # 组合书籍，收集目录
                tocs, book_all = [], []
                parts = ['boks', 'legs', 'fows', 'catalogs', 'bodys', 'atts', 'bacs', 'inss', 'remains']
                for part in parts:
                    if len(collector[part]) != 0:
                        book_all = book_all + collector[part]
                covers = collector['covers']
                if len(covers) == 1:
                    book_all = covers + book_all  # 添加封面
                    tocs.append([1, '封面', 1])    # 添加书签
                elif 2 <= len(covers):
                    book_all = [covers[0]] + book_all + [covers[1]]  # 添加封面
                    tocs.append([1, '封面', 1])                       # 先添加封面书签
                boks = collector['boks']
                if len(boks) != 0:
                    tocs.append([1, '书名', book_all.index(boks[0]) + 1])
                legs = collector['legs']
                if len(legs) != 0:
                    tocs.append([1, '版权', book_all.index(legs[0]) + 1])
                fows = collector['fows']
                if len(fows) != 0:
                    tocs.append([1, '前言', book_all.index(fows[0]) + 1])
                catalogs = collector['catalogs']
                if len(catalogs) != 0:
                    tocs.append([1, '目录', book_all.index(catalogs[0]) + 1])
                # 书籍的主体部分必须要进行特殊处理，以适应书签添加的需要
                bodys = collector['bodys']
                count_temp = len(bodys)
                if count_temp != 0:
                    for body, num in zip(bodys, range(1, count_temp + 1)):
                        tocs.append([1, str(num), book_all.index(body) + 1])
                atts = collector['atts']
                if len(atts) != 0:
                    tocs.append([1, '附录', book_all.index(atts[0]) + 1])
                bacs = collector['bacs']
                if len(bacs) != 0:
                    tocs.append([1, '封底', book_all.index(bacs[0]) + 1])
                inss = collector['inss']
                if len(inss) != 0:
                    tocs.append([1, '插页', book_all.index(inss[0]) + 1])
                remains = collector['remains']
                if len(remains) != 0:
                    tocs.append([1, '未知', book_all.index(remains[0]) + 1])
                if len(covers) == 2:
                    tocs.append([1, '尾页', len(book_all)])   # 最后再添加尾页
                if PAGE_LOST != 0:
                    tocs.append([1, PAGE_LOST, len(book_all)])
                if PAGE_BROKEN != 0:
                    tocs.append([1, PAGE_BROKEN, len(book_all)])
                # 还原为完整路径
                book_all = [join(f'\\\\?\\{root}', part) for part in book_all]      # 启用长路径，防止报错
                # 将重组的书籍图片列表、PDF保存路径、书签信息、删除的文件夹传入pic2pdf
                pic2pdf(book_all, pdf_save_path, tocs, f'\\\\?\\{decompress_dir}')  # 启用长路径，防止报错
                pdf_save_path = pdf_save_path.replace("\\\\?\\", '')
                print('\033[92m' + f'PDF转换成功，保存路径为：{abspath(pdf_save_path)}\n\n' + '\033[0m')


# 调用老马软件处理加密PDG + 损坏无法修复的图片
def pdg2pdf(horses):
    global SAVE_PDG
    # 检测输入、输出路径是否为超长路径
    horses_temp = []
    for index, horse in enumerate(horses, start=1):
        pdg_dir, decompress_dir, pdf_save_path = horse
        pdf_save_path0 = f'\\\\?\\{abspath(pdf_save_path)}'
        # 检测输入路径是否为长路径
        if len(pdg_dir) > 260:
            temp_dir = f'.\\temp{index}'
            if not exists(temp_dir):  # 新建temp文件夹
                makedirs(temp_dir)
            for root, dirs, files in walk(f'\\\\?\\{pdg_dir}'):     # 获取原文件夹下所有文件路径
                pics_old = [join(root, i) for i in files]
                pics_new = [join(temp_dir, i) for i in files]
                break
            for pic_old, pic_new in zip(pics_old, pics_new):        # 将所有图片从原文件夹移动到temp文件夹
                move(pic_old, pic_new)
            if SAVE_PDG == 'SAVE_PDG=FALSE':
                rmtree(f'\\\\?\\{decompress_dir}')                  # 删除原文件夹
            decompress_dir = f'\\\\?\\{abspath(temp_dir)}'          # 将temp_path 指定为临时文件夹
            pdg_dir = abspath(temp_dir)                             # 将输入路径指定为临时文件夹
        # 检测输出路径是否为长路径
        if len(pdf_save_path) > 260:
            pdf_save_path = abspath(f'.\\temp{index}.pdf')
            judge = [1, pdf_save_path, pdf_save_path0]
        else:
            judge = [0]
        horses_temp.append([pdg_dir, decompress_dir, pdf_save_path, judge])
    horses = horses_temp
    # 修改ini文件，不需要确认格式
    with open('.\\Pdg2Pic\\Pdg2Pic.ini', 'a', encoding='UTF-16') as f:    # 防止ini文件缺失
        pass
    contents = ['[ParaBlock]\n', 'PARA_AUTO_CHK=0']
    with open('.\\Pdg2Pic\\Pdg2Pic.ini', 'w', encoding='UTF-16') as f:    # 禁用格式检查
        for i in contents:
            f.write(i)
    # 忽略32位python的警告
    filterwarnings('ignore')
    # 启动软件并设置焦点
    app = Application().start('.\\Pdg2Pic\\Pdg2Pic.exe')
    win = app.window(title='Pdg2Pic', class_name='#32770')
    win.wait("ready", timeout=40, retry_interval=0.1)           # 等待句柄出现
    win.set_focus()
    # 点击转成PDF按钮
    button2pdf = win.child_window(title='或转换出来的PDF文件:', class_name="Button")
    button2pdf.wait("enabled", timeout=10, retry_interval=0.1)   # 确认按钮可点击
    button2pdf.click()
    # 开始逐个处理加密PDG文件夹
    for horse in horses:
        dirpath = horse[0]
        pdf_save_path = horse[2]
        # 输入、输出路径
        for x in range(1, 5):
            # 输入路径
            for j in range(1, 10):
                win.set_focus()
                button_input = win.child_window(title='', class_name="Button", found_index=0)
                button_input.wait("enabled", timeout=10, retry_interval=0.1)   # 确认按钮可点击
                button_input.click()
                win_temp = app.window(title="选择存放PDG文件的文件夹", class_name="#32770")
                judge = win_temp.exists(timeout=15, retry_interval=0.5)        # 等待目标窗口出现，每0.5s检查一次，等待15s后则报超时
                if judge is False:
                    # 有可能会出现点击失效，导致窗口没有出现，进而导致窗口超时的bug，所以超时后要重新尝试
                    print('"选择存放PDG文件的文件夹窗口"，超时不存在！')
                else:
                    for i in range(1, 4):    # 采用 wait 方法速度太慢, 所以直接用 for 循环了
                        try:
                            win_temp_edit = win_temp.child_window(class_name='Edit')
                            win_temp_edit.set_edit_text(dirpath)
                            break
                        except:
                            pass
                    click_check = 0
                    win_temp_button = win_temp.child_window(title="确定", class_name="Button")
                    if win_temp_button.is_enabled():
                        try:    # 如果检测的时候可以点击，但检测完以后就不能点击了，就会报错
                            win_temp_button.click()
                            click_check = 1
                        except:
                            win_temp.close()
                    else:
                        win_temp.close()     # 如果确定按钮因窗口弹出速度太慢而被锁定，则关闭“选择存放PDG文件的文件夹”窗口，重新循环。
                    # 预防一些极其特殊的bug
                    input_path = ''
                    if click_check == 1:
                        for i in range(1, 5):
                            Edit_input = win.child_window(class_name="Edit", found_index=0)
                            if (Edit_input.window_text()).strip() != '':
                                input_path = 'OK'
                                break
                        if input_path == 'OK':
                            break
                        else:
                            judge = win_temp.exists(timeout=1, retry_interval=0.5)
                            if judge:
                                win_temp.close()
            # 输出路径
            eidt_check = 0
            for i in range(1, 10):         # 这个控件因为速度太快，老是存在问题，所以要多设一些
                try:
                    Edit_output = win.child_window(class_name="Edit", found_index=1)
                    Edit_output.set_focus()
                    Edit_output.set_text(pdf_save_path)
                    eidt_check = 1
                    break
                except:
                    judge = win_temp.exists(timeout=0.2, retry_interval=0.1)   # 检查“选择存放PDG文件的文件夹”窗口是否仍然存在, 如果存在的话，应该前面就已经打开了，所以等待时间不宜设置太长，以免影响程序运行效率
                    if judge == True:
                        windows = Desktop().windows()
                        count = []
                        for window in windows:
                            task = (window.window_text()).strip()
                            count.append(task)
                        count = count.count('Pdg2Pic')                        # 检查一下 “Pdg2Pic窗口”是否存在
                        if count == 2:                                        # 说明弹出了报错窗口
                            print('检测出报错窗口')
                            for window in windows:
                                task = (window.window_text()).strip()
                                if task == 'Pdg2Pic':
                                    # 关于有错误的窗口，还得想办法捕捉
                                    if len(window.children()) <= 4:           # 3是不出错的窗口，4是有错误的窗口
                                        window.close()                        # 关闭报错窗口
                                        break
                        win_temp.close()                                      # 关闭“选择存放PDG文件的文件夹”窗口窗口
                        break
            if eidt_check == 1:
                break
        # 点击开始转换
        button_start = win.child_window(title='&4、开始转换', class_name="Button")
        button_start.wait("enabled", timeout=10, retry_interval=0.5)  # 确认按钮可点击
        button_start.click()
        # 监测是否完成
        while True:
            Static_check = win.child_window(class_name="Static", found_index=7)
            Static_text = (Static_check.window_text()).split('/')
            try:                                                              # 防止速度太快而报错，因为有可能会捕捉到变化的间隙，此时值为0
                task = Static_text[0]
                done = Static_text[1]
                if task == done:                                              # 两值相等也就意味着转换完成
                    break
            except:
                pass
        # 等待转换完成或报错窗口
        for i in range(1, 1000):
            windows = Desktop().windows()
            count = []
            for window in windows:
                task = (window.window_text()).strip()
                count.append(task)
            count = count.count('Pdg2Pic')
            if count == 2:
                break
        # 关闭转换完成或报错窗口
        for i in range(1, 10):
            try:
                # 关闭窗口
                windows = Desktop().windows()      # 刷新窗口
                for window in windows:
                    task = (window.window_text()).strip()
                    if task == 'Pdg2Pic':
                        children = window.children()
                        if len(children) == 3:     # 正常窗口是3
                            window.close()
                            break
                        elif len(children) == 4:   # 报错窗口是4, 同样采用close的话，会报超时，一直检测不出来
                            for child in children:
                                if (child.window_text()).strip() == "否(&N)":  # 所以采取了点击否的方式来关闭
                                    child.click()
                                    break
                            break
                # 再次检测
                windows = Desktop().windows()      # 刷新窗口
                count = []
                for window in windows:
                    task = (window.window_text()).strip()
                    count.append(task)
                count = count.count('Pdg2Pic')
                if count == 1:                     # 只有一个窗口的时候，放行
                    break
            except Exception as e:
                print(e)
        print('\033[92m' + f'{basename(horse[1])} 转换成功，保存路径为: {pdf_save_path}' + '\033[0m\n')
    app.kill()
    # 清理文件夹，修改移动PDF文件
    for horse in horses:
        if len(horse[3]) != 1:
            rmtree(horse[1])
            move(horse[3][1], horse[3][2])
        else:
            if SAVE_PDG == 'SAVE_PDG=FALSE':
                rmtree(horse[1])


# 需要重复使用的主程序
def main(package, save_dir):
    file_format = check_file_format(package)                  # 检测文件类型
    if file_format == 'zip':
        check, decompress_dir = check_encryption_zip(package, save_dir)
        if check == 'empty':                                  # 压缩包存在问题（已损坏或者其他问题），直接跳过
            pass
        elif check == 'unencrypted':
            dirs2pdf(decompress_dir)
        elif check == 'encrypted':
            pwds = get_pwds()                                 # 读取解压密码
            check, decompress_dir = decryption_zip(package, save_dir, pwds, 'gbk')
            if check == 'extracted':
                dirs2pdf(decompress_dir)
            elif check == 'None':                             # 如果用gbk编码遍历无结果
                print('gbk遍历无果！尝试用utf-8再次遍历验证！')
                check, decompress_dir = decryption_zip(package, save_dir, pwds, 'utf-8')  # 则用UTF-8再遍历一遍
                if check == 'extracted':
                    dirs2pdf(decompress_dir)
                elif check == 'None':                         # 如果用utf-8再次遍历无果
                    print('utf-8再次遍历无果！\n\n')
                    can_not_break(decompress_dir)
    elif file_format == 'rar':
        check, decompress_dir = check_encryption_rar(package, save_dir)
        if check == 'empty':                                  # 压缩包存在问题（已损坏或者其他问题），直接跳过
            pass
        elif check == 'unencrypted':                          # 非加密的RAR压缩包
            dirs2pdf(decompress_dir)
        elif check == 'encrypted':                            # 如果RAR压缩包已加密
            pwds = get_pwds()                                 # 读取解压密码
            check, decompress_dir = decryption_rar(package, save_dir, pwds, 'gbk')
            if check == 'extracted':
                dirs2pdf(decompress_dir)
            elif check == 'None':
                print('gbk遍历无果！\n')                        # RAR遍历速度太慢了，就不再次遍历了
                can_not_break(decompress_dir)
    elif file_format == '7z':
        check, decompress_dir = check_encryption_7z(package, save_dir)
        if check == 'empty':                                  # 压缩包存在问题
            pass
        elif check == 'unencrypted':                          # 非加密压缩包
            dirs2pdf(decompress_dir)
        elif check == 'encrypted':                            # 加密的7z压缩包
            pwds = get_pwds()                                 # 读取解压密码
            check, decompress_dir = decryption_7z(package, save_dir, pwds, 'gbk')
            if check == 'extracted':
                dirs2pdf(decompress_dir)
            elif check == 'None':
                print('gbk遍历无果！\n')                        # 7z遍历速度太慢了，就不再次遍历了
                can_not_break(decompress_dir)
    elif file_format == 'dir':                                # 从压缩包解压出的文件夹
        decompress_dir = join(save_dir, basename(package))
        move(f'\\\\?\\{package}', f'\\\\?\\{decompress_dir}')     # 将文件夹剪贴到解压文件夹
        copytree(f'\\\\?\\{decompress_dir}', f'\\\\?\\{package}') # 将剪贴好的文件夹复制一份到原路径，保证原数据不丢失
        dirs2pdf(decompress_dir)                                  # 直接传入合成PDF




if __name__ == '__main__':
    print('小白手把手操作说明，大佬可直接略过：\n')
    print('选择一： 将压缩包（ZIP, RAR, UVZ, 7z）或文件夹（含有ZIP, RAR, UVZ, 7z）拖入命令行窗口内获取路径，然后回车运行！\n')
    print('选择二： 鼠标选中压缩包或文件夹后，右键复制文件地址，粘贴到命令行窗口内，然后回车运行！\n')
    print('选择三： 手动输入压缩包或文件夹的路径，然后回车运行！\n')
    print('如果想要批量操作，只需要新建一个文件夹，然后把需要批处理的压缩包或解压后的文件夹（含有PDG，PNG，JPG等图片）放到新建文件夹中，然后再把新建文件夹拖入命令行窗口，最后回车运行，即可自动批量处理！\n')
    SAVE_PDG = check_ini()
    input_path = input('\n\nZIP, RAR, UVZ, 7z或文件夹的路径为：')
    while True:
        packages, horses = [], []  # 需要解密、解压的压缩包文件路径；需要调用老马的软件合成PDF的文件夹路径
        print('\n')
        input_path = input_path.strip('"')  # 如果右键复制文件路径，路径会自动带有"，必须去除后才不会报错
        while True:
            input_path_judge = f'\\\\?\\{input_path}'  # 加上启用长路径，防止路径过长时判断出错
            if isfile(input_path_judge):  # 如果是文件
                package = input_path
                save_dir = input_path.replace(f'.{input_path.split(".")[-1]}', '（PDF）')
                save_dir0 = save_dir
                file_name0 = basename(save_dir).replace('（PDF）', '')
                main(package, save_dir)
                break
            elif isdir(input_path_judge):  # 如果是文件夹
                for root, dirs, files in walk(input_path_judge):  # 则遍历收集文件夹内压缩包文件路径
                    if len(files) != 0:  # 找出压缩包
                        for file in files:
                            if file.endswith(('.zip', '.rar', '.uvz', '.7z')):
                                file_path = join(root.replace('\\\\?\\', ''), file)
                                packages.append(file_path)
                        if len(dirs) == 0:  # 找出文件夹
                            temp_list = []
                            for file in files:
                                if file.endswith(('.pdg', '.png', '.jpg', '.jpeg', '.webp', '.tif', '.tiff')):
                                    temp_list.append(True)
                            if True in temp_list:
                                packages.append(root.replace('\\\\?\\', ''))
                if len(packages) == 0:  # 如果遍历无结果
                    input(f'{abspath(input_path)} 文件夹为空！请先往文件夹内放入压缩包！\n\n请按回车键确认退出：')
                    raise ValueError(f'{abspath(input_path)}文件夹为空！')
                save_dir = f'{input_path}（PDF）'
                save_dir0 = save_dir
                for package in packages:
                    if '.' in package:
                        file_name0 = basename(package.replace('.' + package.split('.')[-1], ''))
                    else:
                        file_name0 = basename(package)
                    main(package, save_dir)
                break
            else:
                print(f'\n{input_path} 路径错误，请仔细检查后重新输入！\n')
                input_path = input('ZIP, RAR, UVZ, 7z或文件夹的路径为：')
        if len(horses) != 0:  # 检查一下是否需要调用老马
            pdg2pdf(horses)
        input_path = input('\n\n可按回车键确认退出，或拖拽新的压缩包、文件夹：')
        if input_path == '':
            break



# TODO
# 加密PDG捕获老马软件导出的loger
# 考虑增加如果没有报错，则直接导出为PDF的功能

